package io.gitee.tziye.core.search;

import io.gitee.tziye.common.Constants;
import io.gitee.tziye.common.RenaiUtil;
import io.gitee.tziye.core.instance.DefaultRenaiClient;
import io.gitee.tziye.enums.SearchType;
import io.gitee.tziye.jdbc.JdbcProxy;
import io.gitee.tziye.jdbc.ProcedureResult;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

public class DefaultSearchOperator extends SearchOperator {

    private final JdbcProxy jdbcProxy;

    public DefaultSearchOperator(DefaultRenaiClient renaiClient) {
        super(renaiClient);
        jdbcProxy = renaiClient.getJdbcProxy();
    }

    @Override
    public void save(RenaiText text) {
        assert text != null && StringUtils.isNoneBlank(text.getModule(), text.getBusinessId())
                && text.getContent() != null;
        commonSave(text.getModule(), List.of(text));
    }

    @Override
    public void save(Collection<RenaiText> texts) {
        assert CollectionUtils.isNotEmpty(texts) && texts.stream().noneMatch(
                t -> StringUtils.isAnyBlank(t.getModule(), t.getBusinessId()) || t.getContent() == null);
        commonSave(new ArrayList<>(texts).get(0).getModule(), texts);
    }

    private void commonSave(String module, Collection<RenaiText> texts) {
        Map<String, Object> params = params(module);
        params.put(Constants.VALUE, RenaiUtil.string(texts));
        jdbcProxy.call(Constants.TEXT_SAVE, params);
    }

    @Override
    public boolean delete(String module, String businessId) {
        assert StringUtils.isNotBlank(module) && StringUtils.isNotBlank(businessId);
        return commonDelete(module, List.of(businessId)) > 0;
    }

    @Override
    public int delete(String module, Collection<String> businessIds) {
        assert StringUtils.isNotBlank(module) && CollectionUtils.isNotEmpty(businessIds);
        return commonDelete(module, businessIds);
    }

    private int commonDelete(String module, Collection<String> businessIds) {
        Map<String, Object> params = params(module);
        params.put(Constants.BUSINESS_ID, RenaiUtil.string(businessIds));
        ProcedureResult result = jdbcProxy.call(Constants.TEXT_DELETE, params);
        return result.updated();
    }

    @Override
    public void drop(String module) {
        assert StringUtils.isNotBlank(module);
        Map<String, Object> params = params(module);
        jdbcProxy.call(Constants.TEXT_DROP, params);
    }

    @Override
    public List<RenaiText> search(String module, SearchType type, String word, boolean withContent, int start, int count) {
        assert StringUtils.isNotBlank(module) && type != null && StringUtils.isNotBlank(word);
        String script = transferToScript(List.of(new SearchOps(type, List.of(word))));
        return commonSearch(module, script, withContent, start, count);
    }

    @Override
    public List<RenaiText> search(String module, SearchType type, List<String> words, boolean withContent, int start, int count) {
        assert StringUtils.isNotBlank(module) && type != null && CollectionUtils.isNotEmpty(words);
        String script = transferToScript(List.of(new SearchOps(type, words)));
        return commonSearch(module, script, withContent, start, count);
    }

    @Override
    public List<RenaiText> search(String module, List<SearchOps> ops, boolean withContent, int start, int count) {
        assert StringUtils.isNotBlank(module) && CollectionUtils.isNotEmpty(ops);
        String script = transferToScript(ops);
        return commonSearch(module, script, withContent, start, count);
    }

    @Override
    public List<RenaiText> search(String module, String script, boolean withContent, int start, int count) {
        assert StringUtils.isNotBlank(module) && StringUtils.isNotBlank(script);
        return commonSearch(module, script, withContent, start, count);
    }

    private List<RenaiText> commonSearch(String module, String script, boolean withContent, int start, int count) {
        Map<String, Object> params = params(module);
        params.put(Constants.SCRIPT, script);
        params.put(Constants.FLAG, withContent ? 1 : -1);
        params.put(Constants.START, start);
        params.put(Constants.SIZE, count);
        ProcedureResult result = jdbcProxy.call(Constants.TEXT_SEARCH, params);
        return result.list(RenaiText.class);
    }

    private Map<String, Object> params(String module) {
        Map<String, Object> params = new HashMap<>();
        params.put(Constants.MODULE, module);
        return params;
    }

    private String transferToScript(List<SearchOps> ops) {
        List<String> modes = ops.stream().map(s -> SearchType.mode(s.getType())).distinct().toList();
        assert modes.size() == 1;
        StringBuilder builder = new StringBuilder();
        builder.append("'");
        for (SearchOps s : ops) {
            String symbol = SearchType.symbol(s.getType());
            for (String word : s.getWords()) {
                if (SearchType.bool_startWith.equals(s.getType())) {
                    builder.append("\"").append(word).append("\"").append(symbol).append(" ");
                } else {
                    builder.append(symbol).append("\"").append(word).append("\"").append(" ");
                }
            }
        }
        builder.append("' ").append(modes.get(0));
        return builder.toString();
    }

}
